package org.aeon.fasttasks.core.manager;

import org.aeon.fasttasks.core.annotation.LoopTask;
import org.aeon.fasttasks.core.annotation.LoopTaskPro;
import org.aeon.fasttasks.core.recorder.TaskRecorder;
import org.aeon.fasttasks.core.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.Future;

/**
 * @Date 2024/9/10 0010 15:51
 * @Author aeon
 * @Description 对任务执行的功能扩展，包括 重试，记录，等待前置任务，跳过等。
 */

public class TaskExpansion implements Runnable {

    private final static Logger log = LoggerFactory.getLogger(TaskExpansion.class);

    /**
     * 任务上下文
     */
    private final TaskContext taskContext;

    /**
     * 任务清单记录器
     */
    private final TaskRecorder taskRecorder;

    /**
     * 准备数据任务执行等待
     */
    private Future<?> future;


    TaskExpansion(TaskContext taskContext, TaskRecorder taskRecorder) {
        this.taskContext = taskContext;
        this.taskRecorder = taskRecorder;
    }


    @Override
    public void run() {
        taskContext.setStart(true);
        waitFrontEndTaskComplete();
        if (!taskContext.isCompleted()) {
            if (Objects.nonNull(future)) {
                try {
                    future.get();
                    try {
                        runExpansion();
                    } catch (Exception e) {
                        errorHandle(e);
                    } finally {
                        loopTasks();
                    }
                } catch (Exception e) {
                    log.error("准备任务执行出错：【{}】", getTaskName(), e);
                }
            }
        }
        complete();
        // 记录任务执行情况 多次相同任务所有任务完成只记录一次
        if (Objects.nonNull(taskRecorder) &&
                taskContext.getMultiStateLatch().getWhenStatusIsUnread()) {
            recordTask();
        }
    }

    /**
     * 等待前置任务完成
     */
    private void waitFrontEndTaskComplete() {
        if (taskContext.getFrontEndTasks() != null) {
            // 获取前置任务bean
            for (TaskExpansion frontEndTask : taskContext.getFrontEndTasks()) {
                if (frontEndTask == null) {
                    continue;
                }
                try {
                    log.info("【{}】等待前置任务执行...", getTaskName());
                    if (!frontEndTask.waitComplete()) {
                        if (!frontEndTask.taskContext.getTask().isIgnoreException()) {
                            throw new RuntimeException("任务异常结束");
                        }
                    }
                } catch (Exception e) {
                    log.error("【{}】前置任务执行失败,跳过后续任务...", getTaskName());
                    taskContext.setCorrect(false);
                    complete();
                }
            }
        }
    }

    /**
     * 环绕增强
     *
     * @throws Exception
     */
    private void runExpansion() throws Exception {
        log.info("任务开始执行：【{}】 - {}", getTaskName(), taskContext.getSerNum());
        taskContext.setStartTime(System.currentTimeMillis());
        taskContext.getTask().runTask(taskContext);
        taskContext.setExecutionTime(System.currentTimeMillis() - taskContext.getStartTime());
        log.info("任务执行结束：【{}】 - {}，耗时：{}ms", getTaskName(), taskContext.getSerNum(),
                taskContext.getExecutionTime());
    }

    /**
     * 任务错误处理
     *
     * @param e
     */
    private void errorHandle(Exception e) {
        if (!taskContext.getTask().isIgnoreException()) {
            log.error("任务执行出错：【{}】", getTaskName(), e);
            if (taskContext.getTask().getRetry() > 0) {
                taskRetry();
            } else {
                taskContext.setCorrect(false);
            }
        }
    }

    /**
     * 循环任务处理
     */
    private void loopTasks() {
        LoopTask loopTask;
        int asyncCount = 1;
        if (Objects.isNull(getAnnotation(LoopTaskPro.class))) {
            loopTask = getAnnotation(LoopTask.class);
        } else {
            LoopTaskPro loopTaskPro = getAnnotation(LoopTaskPro.class);
            loopTask = loopTaskPro.sync();
            asyncCount = Math.max(1, loopTaskPro.async().count());
        }
        if (Objects.nonNull(loopTask) && !loopTask.async() && loopTask.count() > 1) {
            if (loopTask.count() * asyncCount > taskContext.getTaskCounter().get()) {
                if (taskContext.getTask().isRePrepare(taskContext)) {
                    prepareData(true);
                }
                TaskExpansion cloneTask = TaskExpansionFactory.clone(this, taskRecorder);
                taskContext.getTasksScheduler().schedule(() ->
                                taskContext.getBatchWorkTasksExecutor().execute(cloneTask),
                        Instant.now().plus(loopTask.interval(), DateUtil.toChronoUnit(loopTask.timeUnit())));
            }
        }
    }

    /**
     * 任务重试
     */
    private void taskRetry() {
        int retry = taskContext.getTask().getRetry();
        while (retry > 0) {
            try {
                Thread.sleep(taskContext.getTask().getRetryInterval());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("开始重试：【{}】,第{}次", getTaskName(), taskContext.getTask().getRetry() - (--retry));
            try {
                runExpansion();
                break;
            } catch (Exception ex) {
                log.error("任务执行出错：【{}】", getTaskName(), ex);
                if (retry == 0) {
                    taskContext.setCorrect(false);
                }
            }
        }
    }

    /**
     * 设置任务完成状态，并通知所有等待线程
     */
    public void complete() {
        synchronized (this) {
            taskContext.setCompleted(true);
            notifyAll();
            if (!taskContext.getMultiStateLatch().isEmpty()) {
                synchronized (taskContext.getMultiStateLatch()) {
                    taskContext.getMultiStateLatch().notifyAll();
                }
            }
        }
        postCompletionHandle();
    }

    /**
     * 记录任务执行结果
     */
    private void recordTask() {
        taskRecorder.record(
                this.taskContext.getTask().getClass().getName(),
                this.getTaskName(),
                DateUtil.formatTimestamp(this.getTaskContext().getStartTime()),
                this.getTaskContext().isCorrect() ? "正常完成" : "异常终止",
                this.getTaskContext().getExecutionTime()
        );
    }

    /**
     * 确保任务的依赖任务都已经开始执行
     *
     * @return 依赖任务是否都开始执行
     */
    public boolean isTaskCanStart() {
        if (Objects.isNull(taskContext.getFrontEndTasks()) ||
                taskContext.getFrontEndTasks().isEmpty()) {
            return true;
        }
        return taskContext.getFrontEndTasks().stream().allMatch(task -> task.taskContext.isStart());
    }


    /**
     * 获取任务上下文
     *
     * @return
     */
    public TaskContext getTaskContext() {
        return taskContext;
    }

    /**
     * 获取任务注解
     *
     * @param annotationClass
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        return (T) taskContext.getAnnotations().get(annotationClass);
    }

    /**
     * 任务数据准备
     *
     * @throws Exception
     */
    public void prepareData(boolean isRePrepare) {
        try {
            taskContext.getTaskData().putAll(taskContext.getTask().prepareData(taskContext, isRePrepare));
        } catch (Exception e) {
            log.error("任务数据准备出错：【{}】", getTaskName(), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 阻塞调用线程，等待任务结束
     *
     * @return 是否正常完成
     */
    public boolean waitComplete() throws InterruptedException {
        while (true) {
            if (!taskContext.getMultiStateLatch().isEmpty()) {
                synchronized (this) {
                    if (!taskContext.isCompleted()) {
                        wait();
                        continue;
                    }
                }
            }
            synchronized (taskContext.getMultiStateLatch()) {
                if (!taskContext.getMultiStateLatch().getStatus()) {
                    taskContext.getMultiStateLatch().wait();
                    continue;
                }
            }
            return taskContext.isCorrect();
        }
    }

    /**
     * 任务执行后处理
     */
    private void postCompletionHandle() {
        if (taskContext.getMultiStateLatch().getStatus()) {
            taskContext.getCloseables().forEach(closeable -> {
                try {
                    closeable.close();
                } catch (IOException e) {
                    log.error("关闭资源失败", e);
                }
            });
        }
    }

    /**
     * 任务名称
     *
     * @return
     */
    public String getTaskName() {
        return taskContext.getTask().getName();
    }


    Future<?> getFuture() {
        return this.future;
    }

    void setFuture(Future<?> future) {
        this.future = future;
    }

}
